/*
    SEL - Simple DirectMedia Layer Extension Library
    Copyright (C) 2001 Matej Knopp <knopp@users.sf.net>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifndef __sel_cdrom_h__
#define __sel_cdrom_h__

#include "sdl.h"

namespace SEL
{

//! This class represents a CD-ROM drive, and allows to play cd audio.
/*!
  In order to use this class, SEL::init() must have been called
  with the INIT_CDROM flag. This causes SDL to scan the system
  for CD-ROM drives, and load appropriate drivers.
  */
class CDROM
{
public:
	//! Open a CD-ROM drive for access. 
	/*!
	  If the specified drive couldn't be opened (for it was
	  invalid, busy, etc.), an exception is thrown.
	  Drives are numbered starting with 0. Drive 0 is the system
	  default CD-ROM.
	  
	  \param drive   the index of the CD-ROM drive you want to open.
	  		 If omitted, the system default CD-ROM is used.
	 */
	CDROM (int drive = 0);
	
	//! Delete the CD-ROM object, and close the handle for the CD-ROM drive.
	~CDROM ();

	//! Reopen a new CD-ROM, and optionally close the last handle.
	/*!
	  \warning Use this function carefully because you can lose the 
	  previous handle, and not be able to use the CD-ROM.
	  \param drive   the index of the CD-ROM drive you want to open. 
	  		 If omitted, the system default CD-ROM is used.
	  \param close_last whether you want to close the last handle.
	  	  	    It's recommended to do so. Set this to false only
			    if you know, what you are doing.
	 */
	void open (int drive = 0, bool close_last = true);	
	
	//! Close the CD-ROM, and free the handle.
	/*!
	  \warning This function is not recommended to use. But if you
	  do so, make sure that you don't use the object after closing,
	  because it may lead to a crash.
	 */
	void close ();

	//! Return the wrapped SDL::CD structure.
	SDL::CD *cd ()			{ return m_cd; }

	//! Return the wrapped constant SDL::CD structure.
	const SDL::CD *cd () const	{ return m_cd; }
	
	//! Return the current status of the drive.
	/*!
	  If the drive has CD in it, the table of contents of the CD, and
	  current play position of the CD will be stored int the CD::CD
	  wrapped structure. (Use the cd() member funtion to retrieve
	  this structure.)
	  \sa cd ()
	 */
	CDstatus status ();

	//! Play the specified part of the CD.
	/*!
	  Plays the CD starting at \a start_track and \a start_frame
	  for \a ntracks and \a nframes. If both \a ntracks and \a nframes
	  are 0, plays until the end CD. If called without arguments,
	  it plays the entire CD. This function will skip data tracks.
	  For example:
	  \code
	  	// Play entire Cd
		if (cd->inDrive ())
			cd->playTracks ();
		// Play the last track
		if (cd->inDrive ())
			cd->PlayTracks (cd->cd ()->numtracks - 1);
		// Play the first and second track and 10 frames of the third track
		if (cd->inDrive ())
			cd->PlayTracks (0, 0, 2, 10);
	  \endcode
	  \return true, or false if there was an error.
	  */
	  
	bool playTracks (int start_track = 0, int start_frame = 0, 
			 int ntracks = 0, int nframes = 0);
			
	//! Play the specified part of the CD.
	/*!
	  Plays the CD starting at \a start_track for \a ntracks.
	  \sa playTracks (start_track, start_frame, ntracks, nframes);	  
	  \return true, or false if there was an error.
	  */
	bool playTracks (int start_track, int ntracks);
	
	//! Play the CD starting at \a start_frame for \nframes.
	/*!
	  \return true, or false if there was an error.
	  \note You can use the macros FRAMES_TO_MSF (f, M, S, F) 
	  and MSF_TO_FRAMES (M, S, F) to convert frames to
	  Minute/Second/Frames and vice versa.
	 */
	bool play (int start_frame, int nframes);
	
	//! Pause play.
	/*!
	  \return true, or false if there was an error.
	 */ 
	bool pause ();
	
	//! Resume paused play.
	/*!
	  \return true, or false if there was an error.
	 */
	bool resume ();
	
	//! Stop play.
	/*!
	  \return true, or false if there was an error.
	 */
	bool stop ();
	
	//! Eject CD-ROM.
	/*!
	  \return true, or false if there was an error.
	 */
	bool eject ();
	
	//! Query whether there is a CD-ROM in the drive.
	/*!
	  \return true if there is a CD-ROM in the drive, or false if not.
	 */
	bool inDrive ();

	//! Return number of tracks on the current CD.
	/*!
	  \returns the number of tracks on the CD, or -1 if no CD is present.
	  */
	int trackCount();

	//! Returns the track number currently playing.
	/*!
	  \returns the currently playing track number, or -1 if nothing is playing.
	  */
	int currentTrack();

	//! Returns the frame number currently playing.
	/*!
	  \returns the currently playing frame number, or -1 if nothing is playing.
	  */
	int currentFrame();

	//! Return the number of CD-ROM drives on the system.
	/*!
	  If SEL::init has not been called with the INIT_CDROM flag,
	  it returns -1.
	 */
	static int numDrives ();
	
	//! Return the human-readable, system-dependent identifier for
	//! the CD-ROM.
	/*!
	  Example:
	  
	  "/dev/cdrom",
	  
	  "E:",
	  
	  "/dev/disk/ide/1/master"
	  	  
	  \param drive the index of the queried drive
	 */
	const char* name (int drive);

protected:	
	//! The wrapped SDL::CD structure.
	SDL::CD *m_cd;
};

//////////////////////////////////////////////////////////////////
//////////////////////// INLINE FUNCTIONS ////////////////////////
//////////////////////////////////////////////////////////////////
 
inline
CDROM::CDROM (int drive)
{
	m_cd = NULL;
	open (drive);
}
	
inline
CDROM::~CDROM ()
{
	close ();
}

inline void 
CDROM::close ()
{
	if (m_cd)
		SDL::CDClose (m_cd);
}

inline CDstatus 
CDROM::status ()
{
	return SDL::CDStatus (m_cd);
}

inline bool
CDROM::playTracks (int start_track, int start_frame, int ntracks, int nframes)
{
	return SDL::CDPlayTracks (m_cd, start_track, start_frame, 
				  ntracks, nframes) == 0;
}
			
inline bool
CDROM::playTracks (int start_track, int ntracks)
{
	return SDL::CDPlayTracks (m_cd, start_track, 0, ntracks, 0) == 0;
}
	
inline bool
CDROM::play (int start_frame, int nframes)
{
	return SDL::CDPlay (m_cd, start_frame, nframes) == 0;
}
	
inline bool
CDROM::pause ()
{
	return SDL::CDPause (m_cd) == 0;
}
	
inline bool
CDROM::resume ()
{
	return SDL::CDResume (m_cd) == 0;
}
	
inline bool
CDROM::stop ()
{
	return SDL::CDStop (m_cd) == 0;
}
	
inline bool 
CDROM::eject ()
{
	return SDL::CDEject (m_cd) == 0;
}

inline bool 
CDROM::inDrive ()
{
	return CD::INDRIVE (status ());	
}

inline int
CDROM::numDrives ()
{
	return SDL::CDNumDrives ();
}
	
inline const char*
CDROM::name (int drive)
{
	return SDL::CDName (drive);
}

inline int
CDROM::currentTrack()
{
	if(m_cd != NULL)
		return m_cd->cur_track;
	else
		return -1;
}

inline int
CDROM::currentFrame()
{
	if(m_cd != NULL)
		return m_cd->cur_frame;
	else
		return -1;
}

} // namespace SEL

#endif // __sel_cdrom_h__
